package com.zx.idc.backend.gui.api.commom.template;

import com.zx.idc.backend.gui.api.commom.base.BaseIDCCommandService;
import com.zx.idc.backend.gui.api.commom.entity.UploadModel;
import com.zx.idc.backend.gui.api.commom.factory.FtpUploadDataFactory;
import com.zx.idc.backend.gui.api.ftp.FtpOperator;
import com.zx.idc.backend.gui.sys.login.MailClient;
import com.zx.idc.common.constants.DiskStoragePathConstant;
import com.zx.idc.common.disk.FileStorageFacadeExt4;
import com.zx.idc.common.exception.*;
import com.zx.idc.common.lang.PathUtil;
import com.zx.idc.common.spring.SpringConfigUtil;
import com.zx.idc.ds.base.oper.service.IBaseOperCompanyService;
import com.zx.idc.ds.sys.entity.SysUploadFiles;
import com.zx.idc.ds.sys.service.ISysUploadFilesService;
import com.zx.idc.ds.tlog.entity.TLogAlarm;
import com.zx.idc.ds.tlog.factory.TLogAlarmFactory;
import com.zx.idc.ds.tlog.service.ITLogAlarmService;
import com.zx.idc.xml.entity.FileLoad;
import com.zx.idc.xml.utils.JaxbUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;

import static com.zx.idc.common.attributes.SystemAttributes.FTP_RE_UPLOAD_NUM;
import static com.zx.idc.common.lang.StringUtil.append;

/**
 * ftp 模板工具类
 *
 * @author hasee
 * @date 2019/2/26
 */
@Component
public class NIdcFtpTemplate extends BaseIDCCommandService {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private FtpOperator ftpOperator;

    @Autowired
    private ITLogAlarmService itLogAlarmService;

    @Autowired
    private FileStorageFacadeExt4 fileStorageFacadeExt4;

    @Autowired
    private ISysUploadFilesService iSysUploadFilesService;

    @Autowired
    private MailClient mailClient;

    @Autowired
    private IBaseOperCompanyService iBaseOperCompanyService;

    /**
     * 上传 xml 文件模板 统一成功处理(添加到通讯记录)和错误处理(添加到告警表中)
     * 出错抛FTPUploadFailedException
     *
     * @param uploadModel
     * @throws FTPServiceNotExistException
     */
    public void uploadXMLFile(UploadModel uploadModel) {
        log.debug("1.1 check if the ftp server exists...");
        if (ftpOperator == null) {
            throw new FTPUploadFailedException("FTP服务器不存在");
        }
        log.debug("1.2 ftp server is exists...");

        //1.bean2xmlStr
        String uploadDataStr = null;
        String schemaFileName = uploadModel.getSchemaFileName().getSchemaFileName();
        String uploadDir = uploadModel.getUploadDir();
        try {
            log.debug("2.1 bean conversion xmlString start!");
            uploadDataStr = bean2xmlStr(uploadModel.getDataObj(), getSchemaFilePath(schemaFileName));
            log.debug("2.2 bean conversion xmlString done!");
        } catch (SchemaValidateFailedException | SchemaFileNotFoundException | BeanToXmlIOException e) {
            failedProcess(uploadModel, "bean to string xml failed", e);
        }

        //解析上报路径获取上报文件名
        String fileName = PathUtil.getFileName(uploadDir);
        String typeCode = uploadModel.getFileType().getFileType()+"";
        String rootPath = null;
        String path = null;
        //保存源数据
        try {
            log.debug("3.1 save upload file start!");
            rootPath = SpringConfigUtil.get(DiskStoragePathConstant.SOURCE_UPLOAD_FILE_DIRECTORY);
            path = fileStorageFacadeExt4.saveFile(getUploadSourceDataInputStream(uploadModel.getDataObj()), fileName, rootPath);
            log.debug("3.1 save upload file done!");
        } catch (Exception e) {
            failedProcess(uploadModel, "save upload file is failed", e);
        }
        try {
            //根据文件名映射文件路径
            log.debug("4.1 save upload file log start!");
            iSysUploadFilesService.insert(
                new SysUploadFiles()
                    .setFileName(fileName)
                    .setUploadFilePath(path)
                    .setTypeCode(typeCode)
                    .setCreateTime(LocalDateTime.now())
            );
            log.debug("4.1 save upload file log done!");
        } catch (Exception e) {
            failedProcess(uploadModel, "save upload file log is failed", e);
        }
        log.debug("5.1 get fileLoad bean start!");
        String idcId = iBaseOperCompanyService.getIdcId();
        FileLoad fileLoad = FtpUploadDataFactory.getFtpUploadFileLoadEntity(uploadDataStr, idcId);
        if (fileLoad == null) {
            throw new FTPUploadFailedException("加密异常");
        }
        log.debug("5.1 get fileLoad bean done!");
        //5.上传
        log.debug("6.1 fileLoad to xmlFile Stream start !");
        final InputStream in = JaxbUtils.beanToXmlFile(fileLoad);
        if (ObjectUtils.isEmpty(in)) {
            log.error("fileLoad to xmlFile Stream is failed!");
            closeStream(in);
            throw new FTPUploadFailedException("上传转换文件异常");
        }
        log.debug("6.1 fileLoad to xmlFile Stream done !");
        //上传完成
        log.debug("7.1 upload fileLoad start !");
        if (!nUploadFile(in, uploadDir, FTP_RE_UPLOAD_NUM)) {
            failedProcess(uploadModel, "Report upload failed, FTP server is abnormal", new Exception("Report upload failed, FTP server is abnormal"));
        }
        log.debug("7.1 upload fileLoad done !");
        log.debug("8.1 upload finish!");
    }

    @Transactional(rollbackFor = Exception.class)
    public void failedProcess(UploadModel uploadModel, String errorStr, Exception e) {
        log.error(errorStr, e);
        log.debug("record alarm log start!");
        Integer alarmId = insertReportFailTLogAlarm(uploadModel, errorStr);
        log.debug("record alarm log done!");
        log.debug("send alarm email start!");
        mailClient.sendAlarmEmail("有新的告警日志生成，来自上传xml文件", "具体请查看ID为" + alarmId + "的告警日志。");
        log.debug("send alarm email done!");
        throw new FTPUploadFailedException(e.getMessage(), e);
    }

    private boolean nUploadFile(InputStream is, String path, int num) {
        try {
            for (int i = 0; i < num; i++) {
                if (ftpOperator.put(is, path)) {
                    return true;
                }
                log.debug(String.format("执行次数%s", ++i));
            }
            return false;
        } finally {
            closeStream(is);
        }
    }

    private void closeStream(InputStream inputStream) {
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            log.error("关闭输入流异常", e);
        }
    }

    /**
     * 获取上报对象数据的流
     *
     * @param t 上报的源对象
     * @return
     */
    private InputStream getUploadSourceDataInputStream(Object t) {
        return JaxbUtils.beanToXmlFile(t);
    }

    /**
     * 添加上报失败告警记录
     *
     * @param uploadModel
     * @param comments
     */
    private Integer insertReportFailTLogAlarm(UploadModel uploadModel, String comments) {
        TLogAlarm tLogAlarm = null;
        try {
            tLogAlarm = TLogAlarmFactory.getReportFailed(comments);
            itLogAlarmService.insertTLogAlarm(
                tLogAlarm,
                JaxbUtils.beanToXmlFile(uploadModel.getDataObj()),
                uploadModel.getUploadDir()
            );
        } catch (SchemaValidateFailedException | BeanToXmlIOException e) {
            log.error("添加上报失败告警记录异常:保存源文件失败,bean to xml file is failed", e);
            tLogAlarm = TLogAlarmFactory.getReportFailed(comments);
            insertTLogAlarm(tLogAlarm, "保存源文件失败,bean to xml file is failed");
            mailClient.sendAlarmEmail("有新的告警日志生成，来自添加上报失败告警记录", "具体请查看ID为" + tLogAlarm.getId() + "的告警日志。");
        }
        return tLogAlarm.getId();
    }

    /**
     * 添加告警记录
     *
     * @param tLogAlarm 实体
     * @param comments  备注
     */
    private void insertTLogAlarm(TLogAlarm tLogAlarm, String comments) {
        itLogAlarmService.insert(tLogAlarm.setComments(append(tLogAlarm.getComments(), ":", comments)));
    }

}
